/*
 * Copyright (C) 2008 ProSyst Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Based on Peter Kriens tool fileinstall
 * http://www.aqute.biz/Code/Download#fileinstall
 * 
 * Modified by Stefan Maaßen for Apache Felix 1.6.0
 */
package org.modulefusion.dirinstaller.handler;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.modulefusion.dirinstaller.AbstractHandler;
import org.modulefusion.dirinstaller.DirInstallerException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;

public class ConfigFileHandler extends AbstractHandler implements ServiceListener {

    final static String ALIAS_KEY = "_alias_factory_pid";

    private final ServiceTracker configTracker;
    private final List<String> internalCache = new ArrayList<String>();

    public ConfigFileHandler(BundleContext context) {
        super(context);
        configTracker = new ServiceTracker(context, ConfigurationAdmin.class.getName(), null);
    }

    /**
     * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
     */
    public void serviceChanged(ServiceEvent event) {
        if ((event.getType()==ServiceEvent.REGISTERED) && (internalCache.size() > 0) && (configTracker.getService() != null)) { 
            logger.info("found "+ConfigurationAdmin.class.getSimpleName()+" ... adding files");
            synchronized (internalCache) {
                for (String currentFilename : internalCache) {
                    try {
                        installOrUpdate(currentFilename, false);
                    } catch (DirInstallerException e) {
                        logger.error("can't load " + currentFilename + " due to " + e.getMessage(), e);
                    }
                }
                internalCache.clear();
            }
        }
    }

    /**
     * @see org.modulefusion.dirinstaller.AbstractHandler#installOrUpdate(java.lang.String, boolean)
     */
    @Override
    public void installOrUpdate(String fileName, boolean isStartup) throws DirInstallerException {
        ConfigurationAdmin cm = (ConfigurationAdmin) configTracker.getService();
        if (cm == null) {
            logger.info("No "+ConfigurationAdmin.class.getSimpleName()+" found ... adding " + fileName + " to cache");
            synchronized (internalCache) {
                internalCache.add(fileName);
            }
            return;
        }
        logger.info("Installing config: "+fileName);
        File configFile=new File(fileName);
        Properties p = new Properties();
        InputStream in;
        try {
            in = new FileInputStream(configFile);
            p.load(in);
            in.close();
        } catch (FileNotFoundException e) {
            throw new DirInstallerException(e.getMessage());
        } catch (IOException e) {
            throw new DirInstallerException("can't acces "+fileName+" due to "+e.getMessage(), e);
        }

        String pid[] = parsePid(configFile.getName());
        if (pid[1] != null)
            p.put(ALIAS_KEY, pid[1]);
        Configuration config;
        try {
            config = getConfiguration(pid[0], pid[1]);
        } catch (IOException e) {
            throw new DirInstallerException("can't access "+fileName+" due to "+e.getMessage(), e);
        } catch (InvalidSyntaxException e) {
            throw new DirInstallerException("can't load "+fileName+" due to "+e.getMessage(), e);
        }
        if (config.getBundleLocation() != null)
            config.setBundleLocation(null);
        try {
            config.update(p);
        } catch (IOException e) {
            throw new DirInstallerException("can't update config due to "+e.getMessage(), e);
        }
        return;
    }
    
    
    /**
     * @see org.modulefusion.dirinstaller.AbstractHandler#start()
     */
    @Override
    public void start() {
        configTracker.open();
        if (configTracker.getService()!=null) {
            this.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, context.getServiceReference(PackageAdmin.class.getName())));
        }
        context.addServiceListener(this);
    }
    
    /**
     * @see org.modulefusion.dirinstaller.AbstractHandler#stop()
     */
    @Override
    public void stop() {
        context.removeServiceListener(this);
        configTracker.close();
        // clear cache, if possible
        if (internalCache.size()>0) {
            try {
                serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, context.getServiceReference(PackageAdmin.class.getName())));
            } catch (Exception e) {
                logger.warn("can't clear cache due to shutdown of "+context.getBundle().getSymbolicName()+" and due to "+e.getMessage(), e);
            }
        }
    }
    
    /**
     * @see org.modulefusion.dirinstaller.AbstractHandler#handles(java.lang.String)
     */
    @Override
    public boolean handles(String fileName) {
        return fileName.endsWith(".cfg");
    }

    protected String[] parsePid(String path) {
        String pid = path.substring(0, path.length() - 4);
        int n = pid.indexOf('-');
        if (n > 0) {
            String factoryPid = pid.substring(n + 1);
            pid = pid.substring(0, n);
            return new String[] { pid, factoryPid };
        } else
            return new String[] { pid, null };
    }

    protected Configuration getConfiguration(String pid, String factoryPid) throws IOException, InvalidSyntaxException {
        ConfigurationAdmin cm = (ConfigurationAdmin) configTracker.getService();
        if (factoryPid != null) {
            Configuration configs[] = cm.listConfigurations("(|(" + ALIAS_KEY + "=" + factoryPid + ")(.alias_factory_pid=" + factoryPid + "))");
            if (configs == null || configs.length == 0)
                return cm.createFactoryConfiguration(pid, null);
            else
                return configs[0];
        } else
            return cm.getConfiguration(pid, null);
    }
}
